File metadata

Template author: Morgan Carr-Markell
Template last modified on: Feb 28, 2023
Notebook modified by: Katie Wang
Notebook last modified on: Mar 7, 2023

Source

The code and many descriptions in this notebook come from a tutorial posted on the (National Ecological Observatory Network) NEON website and originally from the Data Carpentries. You can find the original tutorial here: https://www.neonscience.org/resources/learning-hub/tutorials/introduction-working-raster-data-r#toggle-26

Raster 03: Raster Calculations in R - Subtract One Raster from Another and Extract Pixel Values For Defined Locations

Original Authors: Leah A. Wasser, Megan A. Jones, Zack Brym, Kristina Riemer, Jason Williams, Jeff Hollister, Mike Smorul

Original Last Updated: Apr 8, 2021

We often want to combine values of and perform calculations on rasters to create a new output raster. This tutorial covers how to subtract one raster from another using basic raster math and the overlay() function.

# load libraries
options("rgdal_show_exportToProj4_warnings"="none") # to suppress warning messages
library(raster)
Loading required package: sp
library(rgdal)
Please note that rgdal will be retired during 2023,
plan transition to sf/stars/terra functions using GDAL and PROJ
at your earliest convenience.
See https://r-spatial.org/r/2022/04/12/evolution.html and https://github.com/r-spatial/evolution
rgdal: version: 1.6-4, (SVN revision 1196)
Geospatial Data Abstraction Library extensions to R successfully loaded
Loaded GDAL runtime: GDAL 3.5.2, released 2022/09/02
Path to GDAL shared files: C:/Users/achro/AppData/Local/R/win-library/4.2/rgdal/gdal
GDAL binary built with GEOS: TRUE 
Loaded PROJ runtime: Rel. 8.2.1, January 1st, 2022, [PJ_VERSION: 821]
Path to PROJ shared files: C:/Users/achro/AppData/Local/R/win-library/4.2/rgdal/proj
PROJ CDN enabled: FALSE
Linking to sp version:1.6-0
To mute warnings of possible GDAL/OSR exportToProj4() degradation,
use options("rgdal_show_exportToProj4_warnings"="none") before loading sp or rgdal.
library(rasterVis)
Loading required package: lattice
library(tidyverse)
Registered S3 methods overwritten by 'dbplyr':
  method         from
  print.tbl_lazy     
  print.tbl_sql      
── Attaching packages ───────────────────────────────────────────────────────────────────────────────────────────── tidyverse 1.3.2 ──✔ ggplot2 3.4.0      ✔ purrr   1.0.1 
✔ tibble  3.1.8      ✔ dplyr   1.0.10
✔ tidyr   1.2.1      ✔ stringr 1.5.0 
✔ readr   2.1.3      ✔ forcats 0.5.2 ── Conflicts ──────────────────────────────────────────────────────────────────────────────────────────────── tidyverse_conflicts() ──
✖ tidyr::extract() masks raster::extract()
✖ dplyr::filter()  masks stats::filter()
✖ dplyr::lag()     masks stats::lag()
✖ dplyr::select()  masks raster::select()
# load the DTM & DSM rasters
DTM_HARV <- raster("NEON-DS-Airborne-Remote-Sensing/HARV/DTM/HARV_dtmCrop.tif")
DSM_HARV <- raster("NEON-DS-Airborne-Remote-Sensing/HARV/DSM/HARV_dsmCrop.tif")

03-A Raster Calculations in R

For example, if we are interested in mapping the heights of trees across an entire field site, we might want to calculate the difference between the Digital Surface Model (DSM, tops of trees) and the Digital Terrain Model (DTM, ground level). The resulting dataset is referred to as a Canopy Height Model (CHM) and represents the actual height of trees, buildings, etc. with the influence of ground elevation removed.

Note: This is an example of local map algebra!

03-B Two Ways to Perform Raster Calculations

We can calculate the difference between two rasters in two different ways:

  • by directly subtracting the two rasters in R using raster math

or for more efficient processing - particularly if our rasters are large and/or the calculations we are performing are complex:

  • using the overlay() function.

03-C Raster Math & Canopy Height Models

We can perform raster calculations by simply subtracting (or adding, multiplying, etc) two rasters. In the geospatial world, we call this “raster math”.

Let’s subtract the DTM from the DSM to create a Canopy Height Model.

# Raster math example
CHM_HARV <- DSM_HARV - DTM_HARV 

# plot the output CHM
plot(CHM_HARV,
     main="Canopy Height Model - Raster Math Subtract\n NEON Harvard Forest Field Site",
     axes=FALSE) 

Let’s have a look at the distribution of values in our newly created Canopy Height Model (CHM).

# histogram of CHM_HARV
hist(CHM_HARV,
  col = "springgreen4",
  main = "Histogram of Canopy Height Model\nNEON Harvard Forest Field Site",
  ylab = "Number of Pixels",
  xlab = "Tree Height (m) ")

  1. (3 pts) What is the range of all the tree heights?
    The tree heights range from 0 to 35.

  2. (3 pts) Briefly describe the distribution (skewed, bimodal, symmetrical)?
    The distribution is skewed left and has two modes: one at 18-20 m and one at 0-2 m.

Note: I am skipping the overlay function section. We will learn about this in future weeks

03-D Export a GeoTIFF

Now that we’ve created a new raster, let’s export the data as a GeoTIFF using the writeRaster() function.

When we write this raster object to a GeoTIFF file we’ll name it chm_HARV.tiff. This name allows us to quickly remember both what the data contains (CHM data) and for where (HARVard Forest). The writeRaster() function by default writes the output file to your working directory unless you specify a full file path.

# create a output subdirectory if it doesn't yet exist
if (!dir.exists("output")){
  dir.create("output")
}
  
# export CHM object to new GeotIFF
writeRaster(CHM_HARV, 
            "output/chm_HARV.tiff",
            format = "GTiff",  # specify output format - GeoTIFF
            overwrite = TRUE, # CAUTION: if this is true, it will overwrite an existing file
            NAflag = -9999) # set no data value to -9999

The function arguments that we used above include:

  • format: specify that the format will be GTiff or GeoTiff.
  • overwrite: If TRUE, R will overwrite any existing file with the same name in the specified directory. USE THIS SETTING WITH CAUTION!
  • NAflag: set the geotiff tag for NoDataValue to -9999, the National Ecological Observatory Network’s (NEON) standard NoDataValue.

Challenge: Explore the NEON San Joaquin Experimental Range Field Site

Data are often more interesting and powerful when we compare them across various locations. Let’s compare some data collected over Harvard Forest to data collected in Southern California. The NEON San Joaquin Experimental Range (SJER) field site located in Southern California has a very different ecosystem and climate than the NEON Harvard Forest Field Site in Massachusetts.

  1. (3 pts) Import the SJER DSM and DTM raster files. Be sure to name your R objects and outputs carefully, as follows: objectType_SJER (e.g. DSM_SJER). This will help you keep track of data from different sites! Import the DSM and DTM from the SJER directory.
DTM_SJER <- raster("NEON-DS-Airborne-Remote-Sensing/SJER/DTM/SJER_dtmCrop.tif")
DSM_SJER <- raster("NEON-DS-Airborne-Remote-Sensing/SJER/DSM/SJER_dsmCrop.tif")
  1. (3 pts) Examine the CRS to make sure that they match:
crs(DTM_SJER)
Coordinate Reference System:
Deprecated Proj.4 representation:
 +proj=utm +zone=11 +datum=WGS84 +units=m +no_defs 
WKT2 2019 representation:
PROJCRS["unknown",
    BASEGEOGCRS["unknown",
        DATUM["World Geodetic System 1984",
            ELLIPSOID["WGS 84",6378137,298.257223563,
                LENGTHUNIT["metre",1]],
            ID["EPSG",6326]],
        PRIMEM["Greenwich",0,
            ANGLEUNIT["degree",0.0174532925199433],
            ID["EPSG",8901]]],
    CONVERSION["UTM zone 11N",
        METHOD["Transverse Mercator",
            ID["EPSG",9807]],
        PARAMETER["Latitude of natural origin",0,
            ANGLEUNIT["degree",0.0174532925199433],
            ID["EPSG",8801]],
        PARAMETER["Longitude of natural origin",-117,
            ANGLEUNIT["degree",0.0174532925199433],
            ID["EPSG",8802]],
        PARAMETER["Scale factor at natural origin",0.9996,
            SCALEUNIT["unity",1],
            ID["EPSG",8805]],
        PARAMETER["False easting",500000,
            LENGTHUNIT["metre",1],
            ID["EPSG",8806]],
        PARAMETER["False northing",0,
            LENGTHUNIT["metre",1],
            ID["EPSG",8807]],
        ID["EPSG",16011]],
    CS[Cartesian,2],
        AXIS["(E)",east,
            ORDER[1],
            LENGTHUNIT["metre",1,
                ID["EPSG",9001]]],
        AXIS["(N)",north,
            ORDER[2],
            LENGTHUNIT["metre",1,
                ID["EPSG",9001]]]] 
crs(DSM_SJER)
Coordinate Reference System:
Deprecated Proj.4 representation:
 +proj=utm +zone=11 +datum=WGS84 +units=m +no_defs 
WKT2 2019 representation:
PROJCRS["unknown",
    BASEGEOGCRS["unknown",
        DATUM["World Geodetic System 1984",
            ELLIPSOID["WGS 84",6378137,298.257223563,
                LENGTHUNIT["metre",1]],
            ID["EPSG",6326]],
        PRIMEM["Greenwich",0,
            ANGLEUNIT["degree",0.0174532925199433],
            ID["EPSG",8901]]],
    CONVERSION["UTM zone 11N",
        METHOD["Transverse Mercator",
            ID["EPSG",9807]],
        PARAMETER["Latitude of natural origin",0,
            ANGLEUNIT["degree",0.0174532925199433],
            ID["EPSG",8801]],
        PARAMETER["Longitude of natural origin",-117,
            ANGLEUNIT["degree",0.0174532925199433],
            ID["EPSG",8802]],
        PARAMETER["Scale factor at natural origin",0.9996,
            SCALEUNIT["unity",1],
            ID["EPSG",8805]],
        PARAMETER["False easting",500000,
            LENGTHUNIT["metre",1],
            ID["EPSG",8806]],
        PARAMETER["False northing",0,
            LENGTHUNIT["metre",1],
            ID["EPSG",8807]],
        ID["EPSG",16011]],
    CS[Cartesian,2],
        AXIS["(E)",east,
            ORDER[1],
            LENGTHUNIT["metre",1,
                ID["EPSG",9001]]],
        AXIS["(N)",north,
            ORDER[2],
            LENGTHUNIT["metre",1,
                ID["EPSG",9001]]]] 
  1. (4 pts) Examine the distributions of values with hist() to look for outliers (you don’t need to use plot titles here, but you can).
hist(DTM_SJER,
     xlab = "Terrain height (m)",
     ylab = "Number of Pixels")
Warning: 2% of the raster cells were used. 100000 values used.

hist(DSM_SJER,
     xlab = "Surface height (m)",
     ylab = "Number of Pixels")
Warning: 2% of the raster cells were used. 100000 values used.

  1. (3 pts) Are there obvious outliers?
    Based on what the histograms show, there are no obvious outliers.

  2. (3 pts) Create a canopy height model (CHM) for SJER. Create a CHM from the two raster layers and check to make sure the data are what you expect.

CHM_SJER = DSM_SJER - DTM_SJER

# checking data by making a histogram
hist(CHM_SJER)

  1. (3 pts) Plot the CHM from SJER and include a descriptive title:
plot(CHM_SJER,
     main="Canopy Height Model - Raster Math Subtract\n NEON San Joaquin Experimental Range Field Site",
     axes = FALSE)

  1. (3 pts) Then plot the CHM_HARV again:
# code copied from above
plot(CHM_HARV,
     main="Canopy Height Model - Raster Math Subtract\n NEON Harvard Forest Field Site",
     axes=FALSE) 

  1. (4 pts) Compare the vegetation structure of the Harvard Forest and San Joaquin Experimental Range. What is different about the distributions of canopy heights at the two NEON sites?
    At the San Joaquin Experimental Range, the trees are for the most part very low-lying, with only a few tall individuals. There’s greater variation in canopy height at the Harvard Forest, and in general the trees are taller there.

  2. (3 pts) Export the SJER CHM as a GeoTIFF in the output subfolder (like we did above for the HARV_ov_CHM but save it as “output/chm_SJER.tiff”):

writeRaster(CHM_SJER, 
            "output/chm_SJER.tiff",
            format = "GTiff",  # GeoTIFF output
            overwrite = TRUE, # will overwrite any existing file
            NAflag = -9999) # no data value is -9999

Optional: Originally, I had thought we would also do Raster 07, but it’s too much for one homework assignment so the rest is just for you to look through if you are interested:

Note: We are skipping Raster 04-06, but if you want to learn more about time series raster data and working with bands you can find those tutorials here

Raster 07: Extract NDVI Summary Values from a Raster Time Series

Original Authors: Leah A. Wasser, Megan A. Jones, Zack Brym, Kristina Riemer, Jason Williams, Jeff Hollister, Mike Smorul

Original Last Updated: Apr 8, 2021

In this tutorial, we will extract NDVI values from a raster time series dataset in R and plot them using ggplot.

If you want to work this tutorial (which, again, is completely optional), you’ll need to first:

  1. download the zipped data folder here
  2. unzip it
  3. move it to this hw5 directory

07-A Extract Summary Statistics From Raster Data

In science, we often want to extract summary values from raster data. For example, we might want to understand overall greeness across a field site or at each plot within a field site. These values can then be compared between different field sites and combined with other related metrics to support modeling and further analysis.

07-B Getting Started

The imagery data used to create this raster teaching data subset were collected over the National Ecological Observatory Network’s Harvard Forest and San Joaquin Experimental Range field sites.

The imagery was created by the U.S. Geological Survey (USGS) using a multispectral scanner on a Landsat Satellite. The data files are Geographic Tagged Image-File Format (GeoTIFF).

Let’s load the data and create a rasterStack first.

# Create list of NDVI file paths
all_HARV_NDVI <- list.files("NEON-DS-Landsat-NDVI/HARV/2011/NDVI",
                            full.names = TRUE,
                            pattern = ".tif$")

# Create a time series raster stack
NDVI_HARV_stack <- stack(all_HARV_NDVI)

# apply scale factor
NDVI_HARV_stack <- NDVI_HARV_stack / 10000

07-C Calculate Average NDVI

Our goal in this tutorial, is to create a data.frame that contains a single, mean NDVI value for each raster in our time series. This value represents the mean NDVI value for this area on a given day.

We can calculate the mean for each raster using the cellStats function. The cellStats function produces a numeric array of values. We can then convert our array format output to a data.frame using as.data.frame().

avg_NDVI_HARV <- NDVI_HARV_stack %>%
  cellStats(mean) %>% # calculate mean NDVI for each raster
  as.data.frame() # convert output array to data.frame

# view data
avg_NDVI_HARV

We now have a data.frame with row.names based on the original file name and a mean NDVI value for each file. Next, let’s clean up the column names in our data.frame to make it easier for colleagues to work with our code.

The column name is not ideal. Let’s change the NDVI column name to MeanNDVI.

# view column name slot
names(avg_NDVI_HARV)
# rename the NDVI column
names(avg_NDVI_HARV) <- "meanNDVI"

# view cleaned column names
names(avg_NDVI_HARV)

While we are only working with one site now, we might want to compare several sites worth of data in the future. Let’s add a column to our data.frame called “site”. We can populate this column with the site name - HARV. Let’s also create a year column and populate it with 2011 - the year our data were collected.

avg_NDVI_HARV <- avg_NDVI_HARV %>%
  mutate(site = "HARV") %>% # add a site column to our data
  mutate(year = "2011") # add a "year" column to our data

# view data
head(avg_NDVI_HARV)

We now have data frame that contains a row for each raster file processed, and a column for meanNDVI, site and year.

07-D Extract Julian Day from row.names

We’d like to produce a plot where Julian days (the numeric day of the year, 0 - 365/366) is on the x-axis and NDVI is on the y-axis. To create this plot, we’ll need a column that contains the Julian day value.

One way to create a Julian day column is to use gsub on the file name in each row. We can replace both the X and the _HARV_NDVI_crop to extract the Julian Day value:

X005_HARV_NDVI_crop

# note the use of the vertical bar character ( | ) is equivalent to "or". This
# allows us to search for more than one pattern in our text strings.
julianDays <- gsub(pattern = "X|_HARV_ndvi_crop", #the pattern to find 
            x = row.names(avg_NDVI_HARV), #the object containing the strings
            replacement = "") #what to replace each instance of the pattern with

# make sure output looks ok
head(julianDays)

And now we can add julianDay values as a column in the data frame:

avg_NDVI_HARV$julianDay <- julianDays

class(avg_NDVI_HARV$julianDay)
  1. What class (type) is the new column?

07-E Convert Julian Day to Date Class

Storing this data as a date object would be better - for plotting, data subsetting and working with our data. Let’s convert.

To convert a Julian Day number to a date class, we need to set the origin of the day which “counting” Julian Days began. Our data are from 2011, and we know that the USGS Landsat Team created Julian Day values for this year. Therefore, the first day or “origin” for our Julian day count is 01 January 2011. Once we set the Julian Day origin, we can add the Julian Day value (as an integer) to the origin date.

Since the origin date was originally set as a Date class object, the new Date column is also stored as class Date.

# set the origin for the julian date (1 Jan 2011)
origin <- as.Date("2011-01-01")

# convert "julianDay" from class character to integer
avg_NDVI_HARV$julianDay <- as.integer(avg_NDVI_HARV$julianDay)

# create a date column; -1 added because origin is the 1st. 
# If we didn't subtract 1: 01/01/2011 + 5 = 01/06/2011 which is Julian day 6, not 5.
avg_NDVI_HARV$Date <- origin + (avg_NDVI_HARV$julianDay - 1)

# did it work? 
head(avg_NDVI_HARV$Date)
  1. What are the classes of the two columns now?
class(avg_NDVI_HARV$Date)

class(avg_NDVI_HARV$julianDay)

Note that when we convert our integer class julianDay values to dates, we subtracted 1 as follows: avg_NDVI_HARV\(Date <- origin + (avg_NDVI_HARV\)julianDay - 1) This is because the origin day is 01 January 2011, so the extracted day is 01. The Julian Day (or year day) for this is also 01. When we convert from the integer 05 julianDay value (indicating 5th of January), we cannot simply add origin + julianDay because 01 + 05 = 06 or 06 January 2011. To correct, this error we then subtract 1 to get the correct day, January 05 2011.

07-F Plot NDVI Using ggplot

We now have a clean data.frame with properly scaled NDVI and Julian days. Let’s plot our data.

We will use the ggplot() function within the ggplot2 package for this plot.

# plot NDVI
ggplot(avg_NDVI_HARV, aes(julianDay, meanNDVI), na.rm=TRUE) +
  geom_point(size=4, color = "PeachPuff4") + 
  labs(title = "Landsat Derived NDVI - 2011\n NEON Harvard Forest Field Site",
       x = "Julian Days",
       y = "Mean NDVI") +
  theme(text = element_text(size=20))

CHALLENGE: PLOT SAN JOAQUIN EXPERIMENTAL RANGE DATA

Create a complementary plot for the SJER data. Plot the data points in a different color.

  1. Create an NDVI_SJER raster stack in the same way that we created the NDVI_HARV raster stack. Use the path: “NEON-DS-Landsat-NDVI/SJER/2011/NDVI” and remember to apply a scaling factor by dividing the stack by 10000 (so that NDVI values are between 0 and 1)
TODO
  1. Summarize it to get an avg_NDVI_SJER as we did above:
TODO
  1. Rename the column to “meanNDVI” and add “site” (SJER) and “year:
TODO
  1. Create “julianDay” and “Date” columns as we did above:
TODO
  1. Use gglot to make a scatter plot of julianDay vs meanNDVI in a different color than “peachPuff4”:
TODO

07-07 Remove Outlier Data

As we look at these plots we see variation in greenness across the year. However, the pattern is interrupted by a few points where NDVI quickly drops towards 0 during a time period when we might expect the vegetation to have a larger greenness value. Is the vegetation truly senescent or gone or are these outlier values that should be removed from the data?

Let’s look at the RGB images from Harvard Forest.

NOTE: the code below uses loops which we will not teach in this tutorial. However the code demonstrates one way to plot multiple RGB rasters in a grid.

Note: This may take a little while to run

# open up RGB imagery
rgb.allCropped <-  list.files("NEON-DS-Landsat-NDVI/HARV/2011/RGB/", 
                              full.names=TRUE, 
                              pattern = ".tif$")
# create a layout
par(mfrow=c(4,4))

# super efficient code to plot RGB image for each day
for (aFile in rgb.allCropped){
  NDVI.rastStack <- stack(aFile)
  plotRGB(NDVI.rastStack, stretch = "lin")
  }

# reset layout
par(mfrow=c(1,1))
  1. Which days (in terms of 1st, 2nd, last, 2nd-to-last) had very heavy cloud cover? These are arranged left to right, top to bottom.

  2. Do you think it makes sense to include NDVI values from those days in an analysis? Why or why not?

Let’s look at the SJER site:

# open up the cropped files
rgb.allCropped.SJER <-  list.files("NEON-DS-Landsat-NDVI/SJER/2011/RGB/", 
                              full.names=TRUE, 
                              pattern = ".tif$")
# create a layout
par(mfrow=c(5,4))

# Super efficient code
# note that there is an issue with one of the rasters
# NEON-DS-Landsat-NDVI/SJER/2011/RGB/254_SJER_landRGB.tif has a blue band with no range
# thus you can't apply a stretch to it. The code below skips the stretch for
# that one image. You could automate this by testing the range of each band in each image

for (aFile in rgb.allCropped.SJER){
  NDVI.rastStack <- stack(aFile)
  if (aFile == "NEON-DS-Landsat-NDVI/SJER/2011/RGB//254_SJER_landRGB.tif"){
    plotRGB(NDVI.rastStack) 
  } else { 
    plotRGB(NDVI.rastStack, stretch="lin") 
  }
}
  1. Which days (in terms of 1st, 2nd, last, 2nd-to-last) had very heavy cloud cover? These are arranged left to right, top to bottom.

Threshold

If we want to only retain points that we think are valid NDVI values, one way to do this is by identifying a threshold value. All values below that threshold will be removed from our analysis. We will use 0.1 as an example for this tutorial. We can then use the subset function to remove outlier datapoints (below our identified threshold).

avg_NDVI_HARV_clean <- avg_NDVI_HARV %>%
  subset(meanNDVI > 0.1)

# Did it work?
avg_NDVI_HARV_clean$meanNDVI < 0.1
  1. Apply the same 0.1 threshold to create a avg_NDVI_SJER_clean data.frame:
avg_NDVI_SJER_clean <- TODO

Now let’s combine the cleaned data frames using rbind:

avg_NDVI_clean <- rbind(avg_NDVI_HARV_clean, avg_NDVI_SJER_clean)
  1. Make a scatter plot showing Date (x) vs. NDVI (y) with the two sites in different colors (use the new avg_NDVI_clean data.frame):
TODO
  1. In which months is NDVI highest and lowest at Harvard Forest? What about San Joaquin Experimental Range?

  2. What do you think might drive the seasonal patterns of NDVI at the two sites?

LS0tDQp0aXRsZTogIlIgTm90ZWJvb2siDQpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sNCi0tLQ0KDQojIyBGaWxlIG1ldGFkYXRhDQoNClRlbXBsYXRlIGF1dGhvcjogTW9yZ2FuIENhcnItTWFya2VsbCAgDQpUZW1wbGF0ZSBsYXN0IG1vZGlmaWVkIG9uOiBGZWIgMjgsIDIwMjMgIA0KTm90ZWJvb2sgbW9kaWZpZWQgYnk6IEthdGllIFdhbmcgIA0KTm90ZWJvb2sgbGFzdCBtb2RpZmllZCBvbjogTWFyIDcsIDIwMjMNCg0KDQojIyBTb3VyY2UNCg0KVGhlIGNvZGUgYW5kIG1hbnkgZGVzY3JpcHRpb25zIGluIHRoaXMgbm90ZWJvb2sgY29tZSBmcm9tIGEgdHV0b3JpYWwgcG9zdGVkIG9uIHRoZSAoTmF0aW9uYWwgRWNvbG9naWNhbCBPYnNlcnZhdG9yeSBOZXR3b3JrKSBORU9OIHdlYnNpdGUgYW5kIG9yaWdpbmFsbHkgZnJvbSB0aGUgRGF0YSBDYXJwZW50cmllcy4gWW91IGNhbiBmaW5kIHRoZSBvcmlnaW5hbCB0dXRvcmlhbCBoZXJlOg0KaHR0cHM6Ly93d3cubmVvbnNjaWVuY2Uub3JnL3Jlc291cmNlcy9sZWFybmluZy1odWIvdHV0b3JpYWxzL2ludHJvZHVjdGlvbi13b3JraW5nLXJhc3Rlci1kYXRhLXIjdG9nZ2xlLTI2DQoNCg0KIyBSYXN0ZXIgMDM6IFJhc3RlciBDYWxjdWxhdGlvbnMgaW4gUiAtIFN1YnRyYWN0IE9uZSBSYXN0ZXIgZnJvbSBBbm90aGVyIGFuZCBFeHRyYWN0IFBpeGVsIFZhbHVlcyBGb3IgRGVmaW5lZCBMb2NhdGlvbnMNCg0KT3JpZ2luYWwgQXV0aG9yczogTGVhaCBBLiBXYXNzZXIsIE1lZ2FuIEEuIEpvbmVzLCBaYWNrIEJyeW0sIEtyaXN0aW5hIFJpZW1lciwgSmFzb24gV2lsbGlhbXMsIEplZmYgSG9sbGlzdGVyLCBNaWtlIFNtb3J1bA0KDQpPcmlnaW5hbCBMYXN0IFVwZGF0ZWQ6IEFwciA4LCAyMDIxDQoNCldlIG9mdGVuIHdhbnQgdG8gY29tYmluZSB2YWx1ZXMgb2YgYW5kIHBlcmZvcm0gY2FsY3VsYXRpb25zIG9uIHJhc3RlcnMgdG8gY3JlYXRlIGEgbmV3IG91dHB1dCByYXN0ZXIuIFRoaXMgdHV0b3JpYWwgY292ZXJzIGhvdyB0byBzdWJ0cmFjdCBvbmUgcmFzdGVyIGZyb20gYW5vdGhlciB1c2luZyBiYXNpYyByYXN0ZXIgbWF0aCBhbmQgdGhlIG92ZXJsYXkoKSBmdW5jdGlvbi4gDQoNCmBgYHtyfQ0KIyBsb2FkIGxpYnJhcmllcw0Kb3B0aW9ucygicmdkYWxfc2hvd19leHBvcnRUb1Byb2o0X3dhcm5pbmdzIj0ibm9uZSIpICMgdG8gc3VwcHJlc3Mgd2FybmluZyBtZXNzYWdlcw0KbGlicmFyeShyYXN0ZXIpDQpsaWJyYXJ5KHJnZGFsKQ0KbGlicmFyeShyYXN0ZXJWaXMpDQpsaWJyYXJ5KHRpZHl2ZXJzZSkNCg0KIyBsb2FkIHRoZSBEVE0gJiBEU00gcmFzdGVycw0KRFRNX0hBUlYgPC0gcmFzdGVyKCJORU9OLURTLUFpcmJvcm5lLVJlbW90ZS1TZW5zaW5nL0hBUlYvRFRNL0hBUlZfZHRtQ3JvcC50aWYiKQ0KRFNNX0hBUlYgPC0gcmFzdGVyKCJORU9OLURTLUFpcmJvcm5lLVJlbW90ZS1TZW5zaW5nL0hBUlYvRFNNL0hBUlZfZHNtQ3JvcC50aWYiKQ0KYGBgDQoNCg0KIyMgMDMtQSBSYXN0ZXIgQ2FsY3VsYXRpb25zIGluIFINCg0KRm9yIGV4YW1wbGUsIGlmIHdlIGFyZSBpbnRlcmVzdGVkIGluIG1hcHBpbmcgdGhlIGhlaWdodHMgb2YgdHJlZXMgYWNyb3NzIGFuIGVudGlyZSBmaWVsZCBzaXRlLCB3ZSBtaWdodCB3YW50IHRvIGNhbGN1bGF0ZSB0aGUgZGlmZmVyZW5jZSBiZXR3ZWVuIHRoZSBEaWdpdGFsIFN1cmZhY2UgTW9kZWwgKERTTSwgdG9wcyBvZiB0cmVlcykgYW5kIHRoZSBEaWdpdGFsIFRlcnJhaW4gTW9kZWwgKERUTSwgZ3JvdW5kIGxldmVsKS4gVGhlIHJlc3VsdGluZyBkYXRhc2V0IGlzIHJlZmVycmVkIHRvIGFzIGEgQ2Fub3B5IEhlaWdodCBNb2RlbCAoQ0hNKSBhbmQgcmVwcmVzZW50cyB0aGUgYWN0dWFsIGhlaWdodCBvZiB0cmVlcywgYnVpbGRpbmdzLCBldGMuIHdpdGggdGhlIGluZmx1ZW5jZSBvZiBncm91bmQgZWxldmF0aW9uIHJlbW92ZWQuDQoNCipOb3RlOiogVGhpcyBpcyBhbiBleGFtcGxlIG9mIGxvY2FsIG1hcCBhbGdlYnJhIQ0KDQoNCiMjIDAzLUIgVHdvIFdheXMgdG8gUGVyZm9ybSBSYXN0ZXIgQ2FsY3VsYXRpb25zDQoNCldlIGNhbiBjYWxjdWxhdGUgdGhlIGRpZmZlcmVuY2UgYmV0d2VlbiB0d28gcmFzdGVycyBpbiB0d28gZGlmZmVyZW50IHdheXM6DQoNCiogYnkgZGlyZWN0bHkgc3VidHJhY3RpbmcgdGhlIHR3byByYXN0ZXJzIGluIFIgdXNpbmcgcmFzdGVyIG1hdGgNCg0Kb3IgZm9yIG1vcmUgZWZmaWNpZW50IHByb2Nlc3NpbmcgLSBwYXJ0aWN1bGFybHkgaWYgb3VyIHJhc3RlcnMgYXJlIGxhcmdlIGFuZC9vciB0aGUgY2FsY3VsYXRpb25zIHdlIGFyZSBwZXJmb3JtaW5nIGFyZSBjb21wbGV4Og0KDQoqIHVzaW5nIHRoZSBvdmVybGF5KCkgZnVuY3Rpb24uDQoNCg0KIyMgMDMtQyBSYXN0ZXIgTWF0aCAmIENhbm9weSBIZWlnaHQgTW9kZWxzDQoNCldlIGNhbiBwZXJmb3JtIHJhc3RlciBjYWxjdWxhdGlvbnMgYnkgc2ltcGx5IHN1YnRyYWN0aW5nIChvciBhZGRpbmcsIG11bHRpcGx5aW5nLCBldGMpIHR3byByYXN0ZXJzLiBJbiB0aGUgZ2Vvc3BhdGlhbCB3b3JsZCwgd2UgY2FsbCB0aGlzICJyYXN0ZXIgbWF0aCIuDQoNCkxldCdzIHN1YnRyYWN0IHRoZSBEVE0gZnJvbSB0aGUgRFNNIHRvIGNyZWF0ZSBhIENhbm9weSBIZWlnaHQgTW9kZWwuDQoNCmBgYHtyfQ0KIyBSYXN0ZXIgbWF0aCBleGFtcGxlDQpDSE1fSEFSViA8LSBEU01fSEFSViAtIERUTV9IQVJWIA0KDQojIHBsb3QgdGhlIG91dHB1dCBDSE0NCnBsb3QoQ0hNX0hBUlYsDQogICAgIG1haW49IkNhbm9weSBIZWlnaHQgTW9kZWwgLSBSYXN0ZXIgTWF0aCBTdWJ0cmFjdFxuIE5FT04gSGFydmFyZCBGb3Jlc3QgRmllbGQgU2l0ZSIsDQogICAgIGF4ZXM9RkFMU0UpIA0KYGBgDQoNCkxldCdzIGhhdmUgYSBsb29rIGF0IHRoZSBkaXN0cmlidXRpb24gb2YgdmFsdWVzIGluIG91ciBuZXdseSBjcmVhdGVkIENhbm9weSBIZWlnaHQgTW9kZWwgKENITSkuDQoNCmBgYHtyfQ0KIyBoaXN0b2dyYW0gb2YgQ0hNX0hBUlYNCmhpc3QoQ0hNX0hBUlYsDQogIGNvbCA9ICJzcHJpbmdncmVlbjQiLA0KICBtYWluID0gIkhpc3RvZ3JhbSBvZiBDYW5vcHkgSGVpZ2h0IE1vZGVsXG5ORU9OIEhhcnZhcmQgRm9yZXN0IEZpZWxkIFNpdGUiLA0KICB5bGFiID0gIk51bWJlciBvZiBQaXhlbHMiLA0KICB4bGFiID0gIlRyZWUgSGVpZ2h0IChtKSAiKQ0KYGBgDQoNCjEuICgzIHB0cykgV2hhdCBpcyB0aGUgcmFuZ2Ugb2YgYWxsIHRoZSB0cmVlIGhlaWdodHM/ICANClRoZSB0cmVlIGhlaWdodHMgcmFuZ2UgZnJvbSAwIHRvIDM1Lg0KICANCjIuICgzIHB0cykgQnJpZWZseSBkZXNjcmliZSB0aGUgZGlzdHJpYnV0aW9uIChza2V3ZWQsIGJpbW9kYWwsIHN5bW1ldHJpY2FsKT8gIA0KVGhlIGRpc3RyaWJ1dGlvbiBpcyBza2V3ZWQgbGVmdCBhbmQgaGFzIHR3byBtb2Rlczogb25lIGF0IDE4LTIwIG0gYW5kIG9uZSBhdCAwLTIgbS4NCg0KDQoqTm90ZToqIEkgYW0gc2tpcHBpbmcgdGhlIG92ZXJsYXkgZnVuY3Rpb24gc2VjdGlvbi4gV2Ugd2lsbCBsZWFybiBhYm91dCB0aGlzIGluIGZ1dHVyZSB3ZWVrcw0KDQoNCiMjIDAzLUQgRXhwb3J0IGEgR2VvVElGRg0KDQpOb3cgdGhhdCB3ZSd2ZSBjcmVhdGVkIGEgbmV3IHJhc3RlciwgbGV0J3MgZXhwb3J0IHRoZSBkYXRhIGFzIGEgR2VvVElGRiB1c2luZyB0aGUgd3JpdGVSYXN0ZXIoKSBmdW5jdGlvbi4NCg0KV2hlbiB3ZSB3cml0ZSB0aGlzIHJhc3RlciBvYmplY3QgdG8gYSBHZW9USUZGIGZpbGUgd2UnbGwgbmFtZSBpdCBjaG1fSEFSVi50aWZmLiBUaGlzIG5hbWUgYWxsb3dzIHVzIHRvIHF1aWNrbHkgcmVtZW1iZXIgYm90aCB3aGF0IHRoZSBkYXRhIGNvbnRhaW5zIChDSE0gZGF0YSkgYW5kIGZvciB3aGVyZSAoSEFSVmFyZCBGb3Jlc3QpLiBUaGUgd3JpdGVSYXN0ZXIoKSBmdW5jdGlvbiBieSBkZWZhdWx0IHdyaXRlcyB0aGUgb3V0cHV0IGZpbGUgdG8geW91ciB3b3JraW5nIGRpcmVjdG9yeSB1bmxlc3MgeW91IHNwZWNpZnkgYSBmdWxsIGZpbGUgcGF0aC4NCg0KYGBge3J9DQojIGNyZWF0ZSBhIG91dHB1dCBzdWJkaXJlY3RvcnkgaWYgaXQgZG9lc24ndCB5ZXQgZXhpc3QNCmlmICghZGlyLmV4aXN0cygib3V0cHV0Iikpew0KICBkaXIuY3JlYXRlKCJvdXRwdXQiKQ0KfQ0KICANCiMgZXhwb3J0IENITSBvYmplY3QgdG8gbmV3IEdlb3RJRkYNCndyaXRlUmFzdGVyKENITV9IQVJWLCANCiAgICAgICAgICAgICJvdXRwdXQvY2htX0hBUlYudGlmZiIsDQogICAgICAgICAgICBmb3JtYXQgPSAiR1RpZmYiLCAgIyBzcGVjaWZ5IG91dHB1dCBmb3JtYXQgLSBHZW9USUZGDQogICAgICAgICAgICBvdmVyd3JpdGUgPSBUUlVFLCAjIENBVVRJT046IGlmIHRoaXMgaXMgdHJ1ZSwgaXQgd2lsbCBvdmVyd3JpdGUgYW4gZXhpc3RpbmcgZmlsZQ0KICAgICAgICAgICAgTkFmbGFnID0gLTk5OTkpICMgc2V0IG5vIGRhdGEgdmFsdWUgdG8gLTk5OTkNCmBgYA0KDQpUaGUgZnVuY3Rpb24gYXJndW1lbnRzIHRoYXQgd2UgdXNlZCBhYm92ZSBpbmNsdWRlOg0KDQoqIGZvcm1hdDogc3BlY2lmeSB0aGF0IHRoZSBmb3JtYXQgd2lsbCBiZSBHVGlmZiBvciBHZW9UaWZmLg0KKiBvdmVyd3JpdGU6IElmIFRSVUUsIFIgd2lsbCBvdmVyd3JpdGUgYW55IGV4aXN0aW5nIGZpbGUgd2l0aCB0aGUgc2FtZSBuYW1lIGluIHRoZSBzcGVjaWZpZWQgZGlyZWN0b3J5LiBVU0UgVEhJUyBTRVRUSU5HIFdJVEggQ0FVVElPTiENCiogTkFmbGFnOiBzZXQgdGhlIGdlb3RpZmYgdGFnIGZvciBOb0RhdGFWYWx1ZSB0byAtOTk5OSwgdGhlIE5hdGlvbmFsIEVjb2xvZ2ljYWwgT2JzZXJ2YXRvcnkgTmV0d29yaydzIChORU9OKSBzdGFuZGFyZCBOb0RhdGFWYWx1ZS4NCg0KDQojIyBDaGFsbGVuZ2U6IEV4cGxvcmUgdGhlIE5FT04gU2FuIEpvYXF1aW4gRXhwZXJpbWVudGFsIFJhbmdlIEZpZWxkIFNpdGUNCg0KRGF0YSBhcmUgb2Z0ZW4gbW9yZSBpbnRlcmVzdGluZyBhbmQgcG93ZXJmdWwgd2hlbiB3ZSBjb21wYXJlIHRoZW0gYWNyb3NzIHZhcmlvdXMgbG9jYXRpb25zLiBMZXQncyBjb21wYXJlIHNvbWUgZGF0YSBjb2xsZWN0ZWQgb3ZlciBIYXJ2YXJkIEZvcmVzdCB0byBkYXRhIGNvbGxlY3RlZCBpbiBTb3V0aGVybiBDYWxpZm9ybmlhLiBUaGUgTkVPTiBTYW4gSm9hcXVpbiBFeHBlcmltZW50YWwgUmFuZ2UgKFNKRVIpIGZpZWxkIHNpdGUgbG9jYXRlZCBpbiBTb3V0aGVybiBDYWxpZm9ybmlhIGhhcyBhIHZlcnkgZGlmZmVyZW50IGVjb3N5c3RlbSBhbmQgY2xpbWF0ZSB0aGFuIHRoZSBORU9OIEhhcnZhcmQgRm9yZXN0IEZpZWxkIFNpdGUgaW4gTWFzc2FjaHVzZXR0cy4NCg0KMy4gKDMgcHRzKSBJbXBvcnQgdGhlIFNKRVIgRFNNIGFuZCBEVE0gcmFzdGVyIGZpbGVzLiBCZSBzdXJlIHRvIG5hbWUgeW91ciBSIG9iamVjdHMgYW5kIG91dHB1dHMgY2FyZWZ1bGx5LCBhcyBmb2xsb3dzOiBvYmplY3RUeXBlX1NKRVIgKGUuZy4gRFNNX1NKRVIpLiBUaGlzIHdpbGwgaGVscCB5b3Uga2VlcCB0cmFjayBvZiBkYXRhIGZyb20gZGlmZmVyZW50IHNpdGVzISBJbXBvcnQgdGhlIERTTSBhbmQgRFRNIGZyb20gdGhlIFNKRVIgZGlyZWN0b3J5Lg0KDQpgYGB7cn0NCkRUTV9TSkVSIDwtIHJhc3RlcigiTkVPTi1EUy1BaXJib3JuZS1SZW1vdGUtU2Vuc2luZy9TSkVSL0RUTS9TSkVSX2R0bUNyb3AudGlmIikNCkRTTV9TSkVSIDwtIHJhc3RlcigiTkVPTi1EUy1BaXJib3JuZS1SZW1vdGUtU2Vuc2luZy9TSkVSL0RTTS9TSkVSX2RzbUNyb3AudGlmIikNCmBgYA0KDQo0LiAoMyBwdHMpIEV4YW1pbmUgdGhlIENSUyB0byBtYWtlIHN1cmUgdGhhdCB0aGV5IG1hdGNoOg0KDQpgYGB7cn0NCmNycyhEVE1fU0pFUikNCmNycyhEU01fU0pFUikNCiMgdGhleSdyZSBib3RoIFVUTQ0KYGBgDQoNCjUuICg0IHB0cykgRXhhbWluZSB0aGUgZGlzdHJpYnV0aW9ucyBvZiB2YWx1ZXMgd2l0aCBoaXN0KCkgdG8gbG9vayBmb3Igb3V0bGllcnMgKHlvdSBkb24ndCBuZWVkIHRvIHVzZSBwbG90IHRpdGxlcyBoZXJlLCBidXQgeW91IGNhbikuDQoNCmBgYHtyfQ0KaGlzdChEVE1fU0pFUiwNCiAgICAgeGxhYiA9ICJUZXJyYWluIGhlaWdodCAobSkiLA0KICAgICB5bGFiID0gIk51bWJlciBvZiBQaXhlbHMiKQ0KYGBgDQoNCmBgYHtyfQ0KaGlzdChEU01fU0pFUiwNCiAgICAgeGxhYiA9ICJTdXJmYWNlIGhlaWdodCAobSkiLA0KICAgICB5bGFiID0gIk51bWJlciBvZiBQaXhlbHMiKQ0KYGBgDQoNCjYuICgzIHB0cykgQXJlIHRoZXJlIG9idmlvdXMgb3V0bGllcnM/ICANCkJhc2VkIG9uIHdoYXQgdGhlIGhpc3RvZ3JhbXMgc2hvdywgdGhlcmUgYXJlIG5vIG9idmlvdXMgb3V0bGllcnMuDQoNCg0KNy4gKDMgcHRzKSBDcmVhdGUgYSBjYW5vcHkgaGVpZ2h0IG1vZGVsIChDSE0pIGZvciBTSkVSLiBDcmVhdGUgYSBDSE0gZnJvbSB0aGUgdHdvIHJhc3RlciBsYXllcnMgYW5kIGNoZWNrIHRvIG1ha2Ugc3VyZSB0aGUgZGF0YSBhcmUgd2hhdCB5b3UgZXhwZWN0Lg0KDQpgYGB7cn0NCkNITV9TSkVSID0gRFNNX1NKRVIgLSBEVE1fU0pFUg0KDQojIGNoZWNraW5nIGRhdGEgYnkgbWFraW5nIGEgaGlzdG9ncmFtDQpoaXN0KENITV9TSkVSKQ0KIyBpdCBsb29rcyByZWFzb25hYmxlDQpgYGANCg0KOC4gKDMgcHRzKSBQbG90IHRoZSBDSE0gZnJvbSBTSkVSIGFuZCBpbmNsdWRlIGEgZGVzY3JpcHRpdmUgdGl0bGU6DQoNCmBgYHtyfQ0KcGxvdChDSE1fU0pFUiwNCiAgICAgbWFpbj0iQ2Fub3B5IEhlaWdodCBNb2RlbCAtIFJhc3RlciBNYXRoIFN1YnRyYWN0XG4gTkVPTiBTYW4gSm9hcXVpbiBFeHBlcmltZW50YWwgUmFuZ2UgRmllbGQgU2l0ZSIsDQogICAgIGF4ZXMgPSBGQUxTRSkNCmBgYA0KDQo5LiAoMyBwdHMpIFRoZW4gcGxvdCB0aGUgQ0hNX0hBUlYgYWdhaW46IA0KDQpgYGB7cn0NCiMgY29kZSBjb3BpZWQgZnJvbSBhYm92ZQ0KcGxvdChDSE1fSEFSViwNCiAgICAgbWFpbj0iQ2Fub3B5IEhlaWdodCBNb2RlbCAtIFJhc3RlciBNYXRoIFN1YnRyYWN0XG4gTkVPTiBIYXJ2YXJkIEZvcmVzdCBGaWVsZCBTaXRlIiwNCiAgICAgYXhlcz1GQUxTRSkgDQpgYGANCg0KMTAuICg0IHB0cykgQ29tcGFyZSB0aGUgdmVnZXRhdGlvbiBzdHJ1Y3R1cmUgb2YgdGhlIEhhcnZhcmQgRm9yZXN0IGFuZCBTYW4gSm9hcXVpbiBFeHBlcmltZW50YWwgUmFuZ2UuIFdoYXQgaXMgZGlmZmVyZW50IGFib3V0IHRoZSBkaXN0cmlidXRpb25zIG9mIGNhbm9weSBoZWlnaHRzIGF0IHRoZSB0d28gTkVPTiBzaXRlcz8gIA0KQXQgdGhlIFNhbiBKb2FxdWluIEV4cGVyaW1lbnRhbCBSYW5nZSwgdGhlIHRyZWVzIGFyZSBmb3IgdGhlIG1vc3QgcGFydCB2ZXJ5IGxvdy1seWluZywgd2l0aCBvbmx5IGEgZmV3IHRhbGwgaW5kaXZpZHVhbHMuIFRoZXJlJ3MgZ3JlYXRlciB2YXJpYXRpb24gaW4gY2Fub3B5IGhlaWdodCBhdCB0aGUgSGFydmFyZCBGb3Jlc3QsIGFuZCBpbiBnZW5lcmFsIHRoZSB0cmVlcyBhcmUgdGFsbGVyIHRoZXJlLiANCg0KDQoxMS4gKDMgcHRzKSBFeHBvcnQgdGhlIFNKRVIgQ0hNIGFzIGEgR2VvVElGRiBpbiB0aGUgb3V0cHV0IHN1YmZvbGRlciAobGlrZSB3ZSBkaWQgYWJvdmUgZm9yIHRoZSBIQVJWX292X0NITSBidXQgc2F2ZSBpdCBhcyAib3V0cHV0L2NobV9TSkVSLnRpZmYiKToNCg0KYGBge3J9DQp3cml0ZVJhc3RlcihDSE1fU0pFUiwgDQogICAgICAgICAgICAib3V0cHV0L2NobV9TSkVSLnRpZmYiLA0KICAgICAgICAgICAgZm9ybWF0ID0gIkdUaWZmIiwgICMgR2VvVElGRiBvdXRwdXQNCiAgICAgICAgICAgIG92ZXJ3cml0ZSA9IFRSVUUsICMgd2lsbCBvdmVyd3JpdGUgYW55IGV4aXN0aW5nIGZpbGUNCiAgICAgICAgICAgIE5BZmxhZyA9IC05OTk5KSAjIG5vIGRhdGEgdmFsdWUgaXMgLTk5OTkNCmBgYA0KDQoNCg0KLS0tLQ0KDQoqKk9wdGlvbmFsOioqIE9yaWdpbmFsbHksIEkgaGFkIHRob3VnaHQgd2Ugd291bGQgYWxzbyBkbyBSYXN0ZXIgMDcsIGJ1dCBpdCdzIHRvbyBtdWNoIGZvciBvbmUgaG9tZXdvcmsgYXNzaWdubWVudCBzbyB0aGUgcmVzdCBpcyBqdXN0IGZvciB5b3UgdG8gbG9vayB0aHJvdWdoIGlmIHlvdSBhcmUgaW50ZXJlc3RlZDoNCg0KDQoqTm90ZToqIFdlIGFyZSBza2lwcGluZyBSYXN0ZXIgMDQtMDYsIGJ1dCBpZiB5b3Ugd2FudCB0byBsZWFybiBtb3JlIGFib3V0IHRpbWUgc2VyaWVzIHJhc3RlciBkYXRhIGFuZCB3b3JraW5nIHdpdGggYmFuZHMgeW91IGNhbiBmaW5kIHRob3NlIHR1dG9yaWFscyBbaGVyZV0oaHR0cHM6Ly93d3cubmVvbnNjaWVuY2Uub3JnL3Jlc291cmNlcy9sZWFybmluZy1odWIvdHV0b3JpYWxzL2ludHJvZHVjdGlvbi13b3JraW5nLXJhc3Rlci1kYXRhLXIjdG9nZ2xlLTMzKQ0KDQoNCiMgUmFzdGVyIDA3OiBFeHRyYWN0IE5EVkkgU3VtbWFyeSBWYWx1ZXMgZnJvbSBhIFJhc3RlciBUaW1lIFNlcmllcw0KDQpPcmlnaW5hbCBBdXRob3JzOiBMZWFoIEEuIFdhc3NlciwgTWVnYW4gQS4gSm9uZXMsIFphY2sgQnJ5bSwgS3Jpc3RpbmEgUmllbWVyLCBKYXNvbiBXaWxsaWFtcywgSmVmZiBIb2xsaXN0ZXIsIE1pa2UgU21vcnVsDQoNCk9yaWdpbmFsIExhc3QgVXBkYXRlZDogQXByIDgsIDIwMjENCg0KSW4gdGhpcyB0dXRvcmlhbCwgd2Ugd2lsbCBleHRyYWN0IE5EVkkgdmFsdWVzIGZyb20gYSByYXN0ZXIgdGltZSBzZXJpZXMgZGF0YXNldCBpbiBSIGFuZCBwbG90IHRoZW0gdXNpbmcgZ2dwbG90Lg0KDQpJZiB5b3Ugd2FudCB0byB3b3JrIHRoaXMgdHV0b3JpYWwgKHdoaWNoLCBhZ2FpbiwgaXMgY29tcGxldGVseSBvcHRpb25hbCksIHlvdSdsbCBuZWVkIHRvIGZpcnN0Og0KDQoxLiBkb3dubG9hZCB0aGUgemlwcGVkIGRhdGEgZm9sZGVyIFtoZXJlXShodHRwczovL2RyaXZlLmdvb2dsZS5jb20vZmlsZS9kLzEwQUItbGJEbmt0dE9CcDJnTmhyVWV3dE9HQWZ5Z0tNNC92aWV3P3VzcD1zaGFyZV9saW5rKQ0KMi4gdW56aXAgaXQNCjMuIG1vdmUgaXQgdG8gdGhpcyBodzUgZGlyZWN0b3J5DQoNCg0KIyMgMDctQSBFeHRyYWN0IFN1bW1hcnkgU3RhdGlzdGljcyBGcm9tIFJhc3RlciBEYXRhDQoNCkluIHNjaWVuY2UsIHdlIG9mdGVuIHdhbnQgdG8gZXh0cmFjdCBzdW1tYXJ5IHZhbHVlcyBmcm9tIHJhc3RlciBkYXRhLiBGb3IgZXhhbXBsZSwgd2UgbWlnaHQgd2FudCB0byB1bmRlcnN0YW5kIG92ZXJhbGwgZ3JlZW5lc3MgYWNyb3NzIGEgZmllbGQgc2l0ZSBvciBhdCBlYWNoIHBsb3Qgd2l0aGluIGEgZmllbGQgc2l0ZS4gVGhlc2UgdmFsdWVzIGNhbiB0aGVuIGJlIGNvbXBhcmVkIGJldHdlZW4gZGlmZmVyZW50IGZpZWxkIHNpdGVzIGFuZCBjb21iaW5lZCB3aXRoIG90aGVyIHJlbGF0ZWQgbWV0cmljcyB0byBzdXBwb3J0IG1vZGVsaW5nIGFuZCBmdXJ0aGVyIGFuYWx5c2lzLg0KDQoNCiMjIDA3LUIgR2V0dGluZyBTdGFydGVkDQoNClRoZSBpbWFnZXJ5IGRhdGEgdXNlZCB0byBjcmVhdGUgdGhpcyByYXN0ZXIgdGVhY2hpbmcgZGF0YSBzdWJzZXQgd2VyZSBjb2xsZWN0ZWQgb3ZlciB0aGUgTmF0aW9uYWwgRWNvbG9naWNhbCBPYnNlcnZhdG9yeSBOZXR3b3JrJ3MgSGFydmFyZCBGb3Jlc3QgYW5kIFNhbiBKb2FxdWluIEV4cGVyaW1lbnRhbCBSYW5nZSBmaWVsZCBzaXRlcy4NCg0KVGhlIGltYWdlcnkgd2FzIGNyZWF0ZWQgYnkgdGhlIFUuUy4gR2VvbG9naWNhbCBTdXJ2ZXkgKFVTR1MpIHVzaW5nIGEgbXVsdGlzcGVjdHJhbCBzY2FubmVyIG9uIGEgTGFuZHNhdCBTYXRlbGxpdGUuIFRoZSBkYXRhIGZpbGVzIGFyZSBHZW9ncmFwaGljIFRhZ2dlZCBJbWFnZS1GaWxlIEZvcm1hdCAoR2VvVElGRikuDQoNCkxldCdzIGxvYWQgdGhlIGRhdGEgYW5kIGNyZWF0ZSBhIHJhc3RlclN0YWNrIGZpcnN0Lg0KDQpgYGB7cn0NCiMgQ3JlYXRlIGxpc3Qgb2YgTkRWSSBmaWxlIHBhdGhzDQphbGxfSEFSVl9ORFZJIDwtIGxpc3QuZmlsZXMoIk5FT04tRFMtTGFuZHNhdC1ORFZJL0hBUlYvMjAxMS9ORFZJIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBmdWxsLm5hbWVzID0gVFJVRSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBwYXR0ZXJuID0gIi50aWYkIikNCg0KIyBDcmVhdGUgYSB0aW1lIHNlcmllcyByYXN0ZXIgc3RhY2sNCk5EVklfSEFSVl9zdGFjayA8LSBzdGFjayhhbGxfSEFSVl9ORFZJKQ0KDQojIGFwcGx5IHNjYWxlIGZhY3Rvcg0KTkRWSV9IQVJWX3N0YWNrIDwtIE5EVklfSEFSVl9zdGFjayAvIDEwMDAwDQpgYGANCg0KDQojIyAwNy1DIENhbGN1bGF0ZSBBdmVyYWdlIE5EVkkNCg0KT3VyIGdvYWwgaW4gdGhpcyB0dXRvcmlhbCwgaXMgdG8gY3JlYXRlIGEgZGF0YS5mcmFtZSB0aGF0IGNvbnRhaW5zIGEgc2luZ2xlLCBtZWFuIE5EVkkgdmFsdWUgZm9yIGVhY2ggcmFzdGVyIGluIG91ciB0aW1lIHNlcmllcy4gVGhpcyB2YWx1ZSByZXByZXNlbnRzIHRoZSBtZWFuIE5EVkkgdmFsdWUgZm9yIHRoaXMgYXJlYSBvbiBhIGdpdmVuIGRheS4NCg0KV2UgY2FuIGNhbGN1bGF0ZSB0aGUgbWVhbiBmb3IgZWFjaCByYXN0ZXIgdXNpbmcgdGhlIGNlbGxTdGF0cyBmdW5jdGlvbi4gVGhlIGNlbGxTdGF0cyBmdW5jdGlvbiBwcm9kdWNlcyBhIG51bWVyaWMgYXJyYXkgb2YgdmFsdWVzLiBXZSBjYW4gdGhlbiBjb252ZXJ0IG91ciBhcnJheSBmb3JtYXQgb3V0cHV0IHRvIGEgZGF0YS5mcmFtZSB1c2luZyBhcy5kYXRhLmZyYW1lKCkuDQoNCmBgYHtyfQ0KYXZnX05EVklfSEFSViA8LSBORFZJX0hBUlZfc3RhY2sgJT4lDQogIGNlbGxTdGF0cyhtZWFuKSAlPiUgIyBjYWxjdWxhdGUgbWVhbiBORFZJIGZvciBlYWNoIHJhc3Rlcg0KICBhcy5kYXRhLmZyYW1lKCkgIyBjb252ZXJ0IG91dHB1dCBhcnJheSB0byBkYXRhLmZyYW1lDQoNCiMgdmlldyBkYXRhDQphdmdfTkRWSV9IQVJWDQpgYGANCg0KV2Ugbm93IGhhdmUgYSBkYXRhLmZyYW1lIHdpdGggcm93Lm5hbWVzIGJhc2VkIG9uIHRoZSBvcmlnaW5hbCBmaWxlIG5hbWUgYW5kIGEgbWVhbiBORFZJIHZhbHVlIGZvciBlYWNoIGZpbGUuIE5leHQsIGxldCdzIGNsZWFuIHVwIHRoZSBjb2x1bW4gbmFtZXMgaW4gb3VyIGRhdGEuZnJhbWUgdG8gbWFrZSBpdCBlYXNpZXIgZm9yIGNvbGxlYWd1ZXMgdG8gd29yayB3aXRoIG91ciBjb2RlLg0KDQpUaGUgY29sdW1uIG5hbWUgaXMgbm90IGlkZWFsLiBMZXQncyBjaGFuZ2UgdGhlIE5EVkkgY29sdW1uIG5hbWUgdG8gTWVhbk5EVkkuDQoNCmBgYHtyfQ0KIyB2aWV3IGNvbHVtbiBuYW1lIHNsb3QNCm5hbWVzKGF2Z19ORFZJX0hBUlYpDQpgYGANCg0KYGBge3J9DQojIHJlbmFtZSB0aGUgTkRWSSBjb2x1bW4NCm5hbWVzKGF2Z19ORFZJX0hBUlYpIDwtICJtZWFuTkRWSSINCg0KIyB2aWV3IGNsZWFuZWQgY29sdW1uIG5hbWVzDQpuYW1lcyhhdmdfTkRWSV9IQVJWKQ0KYGBgDQoNCldoaWxlIHdlIGFyZSBvbmx5IHdvcmtpbmcgd2l0aCBvbmUgc2l0ZSBub3csIHdlIG1pZ2h0IHdhbnQgdG8gY29tcGFyZSBzZXZlcmFsIHNpdGVzIHdvcnRoIG9mIGRhdGEgaW4gdGhlIGZ1dHVyZS4gTGV0J3MgYWRkIGEgY29sdW1uIHRvIG91ciBkYXRhLmZyYW1lIGNhbGxlZCAic2l0ZSIuIFdlIGNhbiBwb3B1bGF0ZSB0aGlzIGNvbHVtbiB3aXRoIHRoZSBzaXRlIG5hbWUgLSBIQVJWLiBMZXQncyBhbHNvIGNyZWF0ZSBhIHllYXIgY29sdW1uIGFuZCBwb3B1bGF0ZSBpdCB3aXRoIDIwMTEgLSB0aGUgeWVhciBvdXIgZGF0YSB3ZXJlIGNvbGxlY3RlZC4NCg0KYGBge3J9DQphdmdfTkRWSV9IQVJWIDwtIGF2Z19ORFZJX0hBUlYgJT4lDQogIG11dGF0ZShzaXRlID0gIkhBUlYiKSAlPiUgIyBhZGQgYSBzaXRlIGNvbHVtbiB0byBvdXIgZGF0YQ0KICBtdXRhdGUoeWVhciA9ICIyMDExIikgIyBhZGQgYSAieWVhciIgY29sdW1uIHRvIG91ciBkYXRhDQoNCiMgdmlldyBkYXRhDQpoZWFkKGF2Z19ORFZJX0hBUlYpDQpgYGANCg0KDQpXZSBub3cgaGF2ZSBkYXRhIGZyYW1lIHRoYXQgY29udGFpbnMgYSByb3cgZm9yIGVhY2ggcmFzdGVyIGZpbGUgcHJvY2Vzc2VkLCBhbmQgYSBjb2x1bW4gZm9yIG1lYW5ORFZJLCBzaXRlIGFuZCB5ZWFyLg0KDQoNCiMjIDA3LUQgRXh0cmFjdCBKdWxpYW4gRGF5IGZyb20gcm93Lm5hbWVzDQoNCldlJ2QgbGlrZSB0byBwcm9kdWNlIGEgcGxvdCB3aGVyZSBKdWxpYW4gZGF5cyAodGhlIG51bWVyaWMgZGF5IG9mIHRoZSB5ZWFyLCAwIC0gMzY1LzM2NikgaXMgb24gdGhlIHgtYXhpcyBhbmQgTkRWSSBpcyBvbiB0aGUgeS1heGlzLiBUbyBjcmVhdGUgdGhpcyBwbG90LCB3ZSdsbCBuZWVkIGEgY29sdW1uIHRoYXQgY29udGFpbnMgdGhlIEp1bGlhbiBkYXkgdmFsdWUuDQoNCk9uZSB3YXkgdG8gY3JlYXRlIGEgSnVsaWFuIGRheSBjb2x1bW4gaXMgdG8gdXNlIGdzdWIgb24gdGhlIGZpbGUgbmFtZSBpbiBlYWNoIHJvdy4gV2UgY2FuIHJlcGxhY2UgYm90aCB0aGUgWCBhbmQgdGhlIF9IQVJWX05EVklfY3JvcCB0byBleHRyYWN0IHRoZSBKdWxpYW4gRGF5IHZhbHVlOg0KDQpYMDA1X0hBUlZfTkRWSV9jcm9wDQoNCmBgYHtyfQ0KIyBub3RlIHRoZSB1c2Ugb2YgdGhlIHZlcnRpY2FsIGJhciBjaGFyYWN0ZXIgKCB8ICkgaXMgZXF1aXZhbGVudCB0byAib3IiLiBUaGlzDQojIGFsbG93cyB1cyB0byBzZWFyY2ggZm9yIG1vcmUgdGhhbiBvbmUgcGF0dGVybiBpbiBvdXIgdGV4dCBzdHJpbmdzLg0KanVsaWFuRGF5cyA8LSBnc3ViKHBhdHRlcm4gPSAiWHxfSEFSVl9uZHZpX2Nyb3AiLCAjdGhlIHBhdHRlcm4gdG8gZmluZCANCiAgICAgICAgICAgIHggPSByb3cubmFtZXMoYXZnX05EVklfSEFSViksICN0aGUgb2JqZWN0IGNvbnRhaW5pbmcgdGhlIHN0cmluZ3MNCiAgICAgICAgICAgIHJlcGxhY2VtZW50ID0gIiIpICN3aGF0IHRvIHJlcGxhY2UgZWFjaCBpbnN0YW5jZSBvZiB0aGUgcGF0dGVybiB3aXRoDQoNCiMgbWFrZSBzdXJlIG91dHB1dCBsb29rcyBvaw0KaGVhZChqdWxpYW5EYXlzKQ0KYGBgDQoNCkFuZCBub3cgd2UgY2FuIGFkZCBqdWxpYW5EYXkgdmFsdWVzIGFzIGEgY29sdW1uIGluIHRoZSBkYXRhIGZyYW1lOg0KDQpgYGB7cn0NCmF2Z19ORFZJX0hBUlYkanVsaWFuRGF5IDwtIGp1bGlhbkRheXMNCg0KY2xhc3MoYXZnX05EVklfSEFSViRqdWxpYW5EYXkpDQpgYGANCg0KMS4gV2hhdCBjbGFzcyAodHlwZSkgaXMgdGhlIG5ldyBjb2x1bW4/DQoNCg0KDQojIyAwNy1FIENvbnZlcnQgSnVsaWFuIERheSB0byBEYXRlIENsYXNzDQoNClN0b3JpbmcgdGhpcyBkYXRhIGFzIGEgZGF0ZSBvYmplY3Qgd291bGQgYmUgYmV0dGVyIC0gZm9yIHBsb3R0aW5nLCBkYXRhIHN1YnNldHRpbmcgYW5kIHdvcmtpbmcgd2l0aCBvdXIgZGF0YS4gTGV0J3MgY29udmVydC4NCg0KVG8gY29udmVydCBhIEp1bGlhbiBEYXkgbnVtYmVyIHRvIGEgZGF0ZSBjbGFzcywgd2UgbmVlZCB0byBzZXQgdGhlIG9yaWdpbiBvZiB0aGUgZGF5IHdoaWNoICJjb3VudGluZyIgSnVsaWFuIERheXMgYmVnYW4uIE91ciBkYXRhIGFyZSBmcm9tIDIwMTEsIGFuZCB3ZSBrbm93IHRoYXQgdGhlIFVTR1MgTGFuZHNhdCBUZWFtIGNyZWF0ZWQgSnVsaWFuIERheSB2YWx1ZXMgZm9yIHRoaXMgeWVhci4gVGhlcmVmb3JlLCB0aGUgZmlyc3QgZGF5IG9yICJvcmlnaW4iIGZvciBvdXIgSnVsaWFuIGRheSBjb3VudCBpcyAwMSBKYW51YXJ5IDIwMTEuIE9uY2Ugd2Ugc2V0IHRoZSBKdWxpYW4gRGF5IG9yaWdpbiwgd2UgY2FuIGFkZCB0aGUgSnVsaWFuIERheSB2YWx1ZSAoYXMgYW4gaW50ZWdlcikgdG8gdGhlIG9yaWdpbiBkYXRlLg0KDQpTaW5jZSB0aGUgb3JpZ2luIGRhdGUgd2FzIG9yaWdpbmFsbHkgc2V0IGFzIGEgRGF0ZSBjbGFzcyBvYmplY3QsIHRoZSBuZXcgRGF0ZSBjb2x1bW4gaXMgYWxzbyBzdG9yZWQgYXMgY2xhc3MgRGF0ZS4NCg0KYGBge3J9DQojIHNldCB0aGUgb3JpZ2luIGZvciB0aGUganVsaWFuIGRhdGUgKDEgSmFuIDIwMTEpDQpvcmlnaW4gPC0gYXMuRGF0ZSgiMjAxMS0wMS0wMSIpDQoNCiMgY29udmVydCAianVsaWFuRGF5IiBmcm9tIGNsYXNzIGNoYXJhY3RlciB0byBpbnRlZ2VyDQphdmdfTkRWSV9IQVJWJGp1bGlhbkRheSA8LSBhcy5pbnRlZ2VyKGF2Z19ORFZJX0hBUlYkanVsaWFuRGF5KQ0KDQojIGNyZWF0ZSBhIGRhdGUgY29sdW1uOyAtMSBhZGRlZCBiZWNhdXNlIG9yaWdpbiBpcyB0aGUgMXN0LiANCiMgSWYgd2UgZGlkbid0IHN1YnRyYWN0IDE6IDAxLzAxLzIwMTEgKyA1ID0gMDEvMDYvMjAxMSB3aGljaCBpcyBKdWxpYW4gZGF5IDYsIG5vdCA1Lg0KYXZnX05EVklfSEFSViREYXRlIDwtIG9yaWdpbiArIChhdmdfTkRWSV9IQVJWJGp1bGlhbkRheSAtIDEpDQoNCiMgZGlkIGl0IHdvcms/IA0KaGVhZChhdmdfTkRWSV9IQVJWJERhdGUpDQpgYGANCg0KMi4gV2hhdCBhcmUgdGhlIGNsYXNzZXMgb2YgdGhlIHR3byBjb2x1bW5zIG5vdz8gDQoNCmBgYHtyfQ0KY2xhc3MoYXZnX05EVklfSEFSViREYXRlKQ0KDQpjbGFzcyhhdmdfTkRWSV9IQVJWJGp1bGlhbkRheSkNCmBgYA0KDQoNCk5vdGUgdGhhdCB3aGVuIHdlIGNvbnZlcnQgb3VyIGludGVnZXIgY2xhc3MganVsaWFuRGF5IHZhbHVlcyB0byBkYXRlcywgd2Ugc3VidHJhY3RlZCAxIGFzIGZvbGxvd3M6IGF2Z19ORFZJX0hBUlYkRGF0ZSA8LSBvcmlnaW4gKyAoYXZnX05EVklfSEFSViRqdWxpYW5EYXkgLSAxKSBUaGlzIGlzIGJlY2F1c2UgdGhlIG9yaWdpbiBkYXkgaXMgMDEgSmFudWFyeSAyMDExLCBzbyB0aGUgZXh0cmFjdGVkIGRheSBpcyAwMS4gVGhlIEp1bGlhbiBEYXkgKG9yIHllYXIgZGF5KSBmb3IgdGhpcyBpcyBhbHNvIDAxLiBXaGVuIHdlIGNvbnZlcnQgZnJvbSB0aGUgaW50ZWdlciAwNSBqdWxpYW5EYXkgdmFsdWUgKGluZGljYXRpbmcgNXRoIG9mIEphbnVhcnkpLCB3ZSBjYW5ub3Qgc2ltcGx5IGFkZCBvcmlnaW4gKyBqdWxpYW5EYXkgYmVjYXVzZSAwMSArIDA1ID0gMDYgb3IgMDYgSmFudWFyeSAyMDExLiBUbyBjb3JyZWN0LCB0aGlzIGVycm9yIHdlIHRoZW4gc3VidHJhY3QgMSB0byBnZXQgdGhlIGNvcnJlY3QgZGF5LCBKYW51YXJ5IDA1IDIwMTEuDQoNCg0KIyMgMDctRiBQbG90IE5EVkkgVXNpbmcgZ2dwbG90DQoNCldlIG5vdyBoYXZlIGEgY2xlYW4gZGF0YS5mcmFtZSB3aXRoIHByb3Blcmx5IHNjYWxlZCBORFZJIGFuZCBKdWxpYW4gZGF5cy4gTGV0J3MgcGxvdCBvdXIgZGF0YS4NCg0KV2Ugd2lsbCB1c2UgdGhlIGdncGxvdCgpIGZ1bmN0aW9uIHdpdGhpbiB0aGUgZ2dwbG90MiBwYWNrYWdlIGZvciB0aGlzIHBsb3QuIA0KDQpgYGB7cn0NCiMgcGxvdCBORFZJDQpnZ3Bsb3QoYXZnX05EVklfSEFSViwgYWVzKGp1bGlhbkRheSwgbWVhbk5EVkkpLCBuYS5ybT1UUlVFKSArDQogIGdlb21fcG9pbnQoc2l6ZT00LCBjb2xvciA9ICJQZWFjaFB1ZmY0IikgKyANCiAgbGFicyh0aXRsZSA9ICJMYW5kc2F0IERlcml2ZWQgTkRWSSAtIDIwMTFcbiBORU9OIEhhcnZhcmQgRm9yZXN0IEZpZWxkIFNpdGUiLA0KICAgICAgIHggPSAiSnVsaWFuIERheXMiLA0KICAgICAgIHkgPSAiTWVhbiBORFZJIikgKw0KICB0aGVtZSh0ZXh0ID0gZWxlbWVudF90ZXh0KHNpemU9MjApKQ0KYGBgDQoNCg0KIyMgQ0hBTExFTkdFOiBQTE9UIFNBTiBKT0FRVUlOIEVYUEVSSU1FTlRBTCBSQU5HRSBEQVRBDQoNCkNyZWF0ZSBhIGNvbXBsZW1lbnRhcnkgcGxvdCBmb3IgdGhlIFNKRVIgZGF0YS4gUGxvdCB0aGUgZGF0YSBwb2ludHMgaW4gYSBkaWZmZXJlbnQgY29sb3IuDQoNCjMuIENyZWF0ZSBhbiBORFZJX1NKRVIgcmFzdGVyIHN0YWNrIGluIHRoZSBzYW1lIHdheSB0aGF0IHdlIGNyZWF0ZWQgdGhlIE5EVklfSEFSViByYXN0ZXIgc3RhY2suIFVzZSB0aGUgcGF0aDogIk5FT04tRFMtTGFuZHNhdC1ORFZJL1NKRVIvMjAxMS9ORFZJIiBhbmQgcmVtZW1iZXIgdG8gYXBwbHkgYSBzY2FsaW5nIGZhY3RvciBieSBkaXZpZGluZyB0aGUgc3RhY2sgYnkgMTAwMDAgKHNvIHRoYXQgTkRWSSB2YWx1ZXMgYXJlIGJldHdlZW4gMCBhbmQgMSkNCg0KYGBge3J9DQpUT0RPDQpgYGANCg0KNC4gU3VtbWFyaXplIGl0IHRvIGdldCBhbiBhdmdfTkRWSV9TSkVSIGFzIHdlIGRpZCBhYm92ZToNCg0KYGBge3J9DQpUT0RPDQpgYGANCg0KNS4gUmVuYW1lIHRoZSBjb2x1bW4gdG8gIm1lYW5ORFZJIiBhbmQgYWRkICJzaXRlIiAoU0pFUikgYW5kICJ5ZWFyOg0KDQpgYGB7cn0NClRPRE8NCmBgYA0KDQo2LiBDcmVhdGUgImp1bGlhbkRheSIgYW5kICJEYXRlIiBjb2x1bW5zIGFzIHdlIGRpZCBhYm92ZToNCg0KYGBge3J9DQpUT0RPDQpgYGANCg0KNy4gVXNlIGdnbG90IHRvIG1ha2UgYSBzY2F0dGVyIHBsb3Qgb2YganVsaWFuRGF5IHZzIG1lYW5ORFZJIGluIGEgZGlmZmVyZW50IGNvbG9yIHRoYW4gInBlYWNoUHVmZjQiOg0KDQpgYGB7cn0NClRPRE8NCmBgYA0KDQoNCiMjIDA3LTA3IFJlbW92ZSBPdXRsaWVyIERhdGENCg0KQXMgd2UgbG9vayBhdCB0aGVzZSBwbG90cyB3ZSBzZWUgdmFyaWF0aW9uIGluIGdyZWVubmVzcyBhY3Jvc3MgdGhlIHllYXIuIEhvd2V2ZXIsIHRoZSBwYXR0ZXJuIGlzIGludGVycnVwdGVkIGJ5IGEgZmV3IHBvaW50cyB3aGVyZSBORFZJIHF1aWNrbHkgZHJvcHMgdG93YXJkcyAwIGR1cmluZyBhIHRpbWUgcGVyaW9kIHdoZW4gd2UgbWlnaHQgZXhwZWN0IHRoZSB2ZWdldGF0aW9uIHRvIGhhdmUgYSBsYXJnZXIgZ3JlZW5uZXNzIHZhbHVlLiBJcyB0aGUgdmVnZXRhdGlvbiB0cnVseSBzZW5lc2NlbnQgb3IgZ29uZSBvciBhcmUgdGhlc2Ugb3V0bGllciB2YWx1ZXMgdGhhdCBzaG91bGQgYmUgcmVtb3ZlZCBmcm9tIHRoZSBkYXRhPw0KDQpMZXQncyBsb29rIGF0IHRoZSBSR0IgaW1hZ2VzIGZyb20gSGFydmFyZCBGb3Jlc3QuDQoNCk5PVEU6IHRoZSBjb2RlIGJlbG93IHVzZXMgbG9vcHMgd2hpY2ggd2Ugd2lsbCBub3QgdGVhY2ggaW4gdGhpcyB0dXRvcmlhbC4gSG93ZXZlciB0aGUgY29kZSBkZW1vbnN0cmF0ZXMgb25lIHdheSB0byBwbG90IG11bHRpcGxlIFJHQiByYXN0ZXJzIGluIGEgZ3JpZC4NCg0KKk5vdGU6KiBUaGlzIG1heSB0YWtlIGEgbGl0dGxlIHdoaWxlIHRvIHJ1bg0KDQpgYGB7cn0NCiMgb3BlbiB1cCBSR0IgaW1hZ2VyeQ0KcmdiLmFsbENyb3BwZWQgPC0gIGxpc3QuZmlsZXMoIk5FT04tRFMtTGFuZHNhdC1ORFZJL0hBUlYvMjAxMS9SR0IvIiwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmdWxsLm5hbWVzPVRSVUUsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcGF0dGVybiA9ICIudGlmJCIpDQojIGNyZWF0ZSBhIGxheW91dA0KcGFyKG1mcm93PWMoNCw0KSkNCg0KIyBzdXBlciBlZmZpY2llbnQgY29kZSB0byBwbG90IFJHQiBpbWFnZSBmb3IgZWFjaCBkYXkNCmZvciAoYUZpbGUgaW4gcmdiLmFsbENyb3BwZWQpew0KICBORFZJLnJhc3RTdGFjayA8LSBzdGFjayhhRmlsZSkNCiAgcGxvdFJHQihORFZJLnJhc3RTdGFjaywgc3RyZXRjaCA9ICJsaW4iKQ0KICB9DQoNCiMgcmVzZXQgbGF5b3V0DQpwYXIobWZyb3c9YygxLDEpKQ0KYGBgDQoNCjguIFdoaWNoIGRheXMgKGluIHRlcm1zIG9mIDFzdCwgMm5kLCBsYXN0LCAybmQtdG8tbGFzdCkgaGFkIHZlcnkgaGVhdnkgY2xvdWQgY292ZXI/IFRoZXNlIGFyZSBhcnJhbmdlZCBsZWZ0IHRvIHJpZ2h0LCB0b3AgdG8gYm90dG9tLg0KDQoNCg0KOS4gRG8geW91IHRoaW5rIGl0IG1ha2VzIHNlbnNlIHRvIGluY2x1ZGUgTkRWSSB2YWx1ZXMgZnJvbSB0aG9zZSBkYXlzIGluIGFuIGFuYWx5c2lzPyBXaHkgb3Igd2h5IG5vdD8NCg0KDQoNCkxldCdzIGxvb2sgYXQgdGhlIFNKRVIgc2l0ZToNCg0KYGBge3J9DQojIG9wZW4gdXAgdGhlIGNyb3BwZWQgZmlsZXMNCnJnYi5hbGxDcm9wcGVkLlNKRVIgPC0gIGxpc3QuZmlsZXMoIk5FT04tRFMtTGFuZHNhdC1ORFZJL1NKRVIvMjAxMS9SR0IvIiwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmdWxsLm5hbWVzPVRSVUUsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcGF0dGVybiA9ICIudGlmJCIpDQojIGNyZWF0ZSBhIGxheW91dA0KcGFyKG1mcm93PWMoNSw0KSkNCg0KIyBTdXBlciBlZmZpY2llbnQgY29kZQ0KIyBub3RlIHRoYXQgdGhlcmUgaXMgYW4gaXNzdWUgd2l0aCBvbmUgb2YgdGhlIHJhc3RlcnMNCiMgTkVPTi1EUy1MYW5kc2F0LU5EVkkvU0pFUi8yMDExL1JHQi8yNTRfU0pFUl9sYW5kUkdCLnRpZiBoYXMgYSBibHVlIGJhbmQgd2l0aCBubyByYW5nZQ0KIyB0aHVzIHlvdSBjYW4ndCBhcHBseSBhIHN0cmV0Y2ggdG8gaXQuIFRoZSBjb2RlIGJlbG93IHNraXBzIHRoZSBzdHJldGNoIGZvcg0KIyB0aGF0IG9uZSBpbWFnZS4gWW91IGNvdWxkIGF1dG9tYXRlIHRoaXMgYnkgdGVzdGluZyB0aGUgcmFuZ2Ugb2YgZWFjaCBiYW5kIGluIGVhY2ggaW1hZ2UNCg0KZm9yIChhRmlsZSBpbiByZ2IuYWxsQ3JvcHBlZC5TSkVSKXsNCiAgTkRWSS5yYXN0U3RhY2sgPC0gc3RhY2soYUZpbGUpDQogIGlmIChhRmlsZSA9PSAiTkVPTi1EUy1MYW5kc2F0LU5EVkkvU0pFUi8yMDExL1JHQi8vMjU0X1NKRVJfbGFuZFJHQi50aWYiKXsNCiAgICBwbG90UkdCKE5EVkkucmFzdFN0YWNrKSANCiAgfSBlbHNlIHsgDQogICAgcGxvdFJHQihORFZJLnJhc3RTdGFjaywgc3RyZXRjaD0ibGluIikgDQogIH0NCn0NCmBgYA0KDQoxMC4gV2hpY2ggZGF5cyAoaW4gdGVybXMgb2YgMXN0LCAybmQsIGxhc3QsIDJuZC10by1sYXN0KSBoYWQgdmVyeSBoZWF2eSBjbG91ZCBjb3Zlcj8gVGhlc2UgYXJlIGFycmFuZ2VkIGxlZnQgdG8gcmlnaHQsIHRvcCB0byBib3R0b20uDQoNCg0KDQojIyMgVGhyZXNob2xkDQoNCklmIHdlIHdhbnQgdG8gb25seSByZXRhaW4gcG9pbnRzIHRoYXQgd2UgdGhpbmsgYXJlIHZhbGlkIE5EVkkgdmFsdWVzLCBvbmUgd2F5IHRvIGRvIHRoaXMgaXMgYnkgaWRlbnRpZnlpbmcgYSB0aHJlc2hvbGQgdmFsdWUuIEFsbCB2YWx1ZXMgYmVsb3cgdGhhdCB0aHJlc2hvbGQgd2lsbCBiZSByZW1vdmVkIGZyb20gb3VyIGFuYWx5c2lzLiBXZSB3aWxsIHVzZSAwLjEgYXMgYW4gZXhhbXBsZSBmb3IgdGhpcyB0dXRvcmlhbC4gV2UgY2FuIHRoZW4gdXNlIHRoZSBzdWJzZXQgZnVuY3Rpb24gdG8gcmVtb3ZlIG91dGxpZXIgZGF0YXBvaW50cyAoYmVsb3cgb3VyIGlkZW50aWZpZWQgdGhyZXNob2xkKS4NCg0KYGBge3J9DQphdmdfTkRWSV9IQVJWX2NsZWFuIDwtIGF2Z19ORFZJX0hBUlYgJT4lDQogIHN1YnNldChtZWFuTkRWSSA+IDAuMSkNCg0KIyBEaWQgaXQgd29yaz8NCmF2Z19ORFZJX0hBUlZfY2xlYW4kbWVhbk5EVkkgPCAwLjENCmBgYA0KDQoxMS4gQXBwbHkgdGhlIHNhbWUgMC4xIHRocmVzaG9sZCB0byBjcmVhdGUgYSBhdmdfTkRWSV9TSkVSX2NsZWFuIGRhdGEuZnJhbWU6DQoNCmBgYHtyfQ0KYXZnX05EVklfU0pFUl9jbGVhbiA8LSBUT0RPDQpgYGANCg0KDQpOb3cgbGV0J3MgY29tYmluZSB0aGUgY2xlYW5lZCBkYXRhIGZyYW1lcyB1c2luZyByYmluZDoNCg0KYGBge3J9DQphdmdfTkRWSV9jbGVhbiA8LSByYmluZChhdmdfTkRWSV9IQVJWX2NsZWFuLCBhdmdfTkRWSV9TSkVSX2NsZWFuKQ0KYGBgDQoNCjEyLiBNYWtlIGEgc2NhdHRlciBwbG90IHNob3dpbmcgRGF0ZSAoeCkgdnMuIE5EVkkgKHkpIHdpdGggdGhlIHR3byBzaXRlcyBpbiBkaWZmZXJlbnQgY29sb3JzICh1c2UgdGhlIG5ldyBhdmdfTkRWSV9jbGVhbiBkYXRhLmZyYW1lKToNCg0KYGBge3J9DQpUT0RPDQpgYGANCg0KDQoxMy4gSW4gd2hpY2ggbW9udGhzIGlzIE5EVkkgaGlnaGVzdCBhbmQgbG93ZXN0IGF0IEhhcnZhcmQgRm9yZXN0PyBXaGF0IGFib3V0IFNhbiBKb2FxdWluIEV4cGVyaW1lbnRhbCBSYW5nZT8NCg0KDQoNCjE0LiBXaGF0IGRvIHlvdSB0aGluayBtaWdodCBkcml2ZSB0aGUgc2Vhc29uYWwgcGF0dGVybnMgb2YgTkRWSSBhdCB0aGUgdHdvIHNpdGVzPw0KDQo=